package com.py.payment.nettys.handle;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.py.payment.entity.Monitor;
import com.py.payment.entity.PointPosition;
import com.py.payment.nettys.cilent.NettyClient;
import com.py.payment.nettys.constant.MessageConstant;
import com.py.payment.nettys.enmus.DeviceEnum;
import com.py.payment.nettys.server.ChannelMap;
import com.py.payment.service.MonitorService;
import com.py.payment.service.PointPositionService;
import com.py.payment.tools.ToolsUtils;
import com.py.payment.tools.bean.SpringBeanUtils;
import com.py.payment.tools.date.DateUtils;
import com.py.payment.tools.dtu.CharacterConvertUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    private ThreadPoolTaskExecutor asyncServiceExecutor;

    private PointPositionService pointPositionService;

    private MonitorService monitorService;

    private NettyClient nettyClient;

    /**
     * 有客户端连接服务器会触发此函数
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();
        int clientPort = insocket.getPort();
        //获取连接通道唯一标识
        ChannelId channelId = ctx.channel().id();
        //如果map中不包含此连接，就保存连接
        if (ChannelMap.getChannelMap().containsKey(channelId)) {
            log.info("客户端:{},是连接状态，连接通道数量:{} ",channelId,ChannelMap.getChannelMap().size());
        } else {
            //保存连接
            ChannelMap.addChannel(channelId, ctx.channel());
            log.info("客户端:{},连接netty服务器[IP:{}-->PORT:{}]",channelId, clientIp,clientPort);
            log.info("连接通道数量: {}",ChannelMap.getChannelMap().size());
        }
    }

    /**
     * 有客户端终止连接服务器会触发此函数
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        ChannelId channelId = ctx.channel().id();
        //包含此客户端才去删除
        if (ChannelMap.getChannelMap().containsKey(channelId)) {
            //删除连接
            ChannelMap.getChannelMap().remove(channelId);
            log.info("客户端:{},连接netty服务器[IP:{}-->PORT:{}]",channelId, clientIp,inSocket.getPort());
            log.info("连接通道数量: " + ChannelMap.getChannelMap().size());
        }
    }

    /**
     * 有客户端发消息会触发此函数
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        log.info("加载客户端报文,客户端id:{},客户端消息:{}",ctx.channel().id(), msg);
//        String collectionCode = (String) msg;
//
//        if(collectionCode.length()<4){
//            log.info("接收到消息，但是不够起始字头：{}",collectionCode);
//            return;
//        }

        String collectionCode = "";
        ByteBuf byteBuf = (ByteBuf) msg;
        if(byteBuf.hasArray()){
            collectionCode = new String(byteBuf.array(),byteBuf.arrayOffset()+byteBuf.readerIndex(),byteBuf.readableBytes());
        } else {
            byte[] bytes = new byte[byteBuf.readableBytes()];
            byteBuf.getBytes(byteBuf.readerIndex(),bytes);
            collectionCode = new String(bytes,0,byteBuf.readableBytes());
        }

        log.info("加载客户端报文,客户端id:{},客户端消息:{}",ctx.channel().id(), collectionCode);

        if(collectionCode.length()<4){
            log.info("接收到消息，但是不够起始字头：{}",collectionCode);
            return;
        }

        if(MessageConstant.heartBeat.equals(collectionCode)){
            Channel channel = ctx.channel();
            log.info("接收到心跳信息" + channel.id());
            if (ctx.channel().isActive()) {
                ctx.writeAndFlush(collectionCode);
            }
            return;
        }

        int count = collectionCode.length()/2;
        int x = 0;
        int y = 2;
        List<String> list = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            list.add(collectionCode.substring(x, y));
            x = y;
            y = y + 2;
        }
        String header = list.get(0)+list.get(1);
        if(MessageConstant.replyFrameStartingPrefix.equals(header)){
            asyncServiceExecutor = (ThreadPoolTaskExecutor) SpringBeanUtils.getBeanByName("asyncServiceExecutor");
            asyncServiceExecutor.execute(()->{
                String devicId = String.join("",list.subList(6,14));
                String datetime = String.join("",list.subList(14,20));
                String measurementValue =  String.join("",list.subList(22,24));
                Integer[] companyValue = new Integer[]{
                        Integer.parseInt(list.get(30),16),
                        Integer.parseInt(list.get(31),16),
                        Integer.parseInt(list.get(32),16),
                        Integer.parseInt(list.get(33),16)
                };
                String type = list.get(34);
                BigDecimal value = new BigDecimal(Integer.parseInt(measurementValue,16));
                if(DeviceEnum.displacement_meter.getCode().equals(type)){
                    value = value.multiply(new BigDecimal(0.1)).setScale(2, BigDecimal.ROUND_HALF_UP);
                }
                String company = CharacterConvertUtils.parseAsciiToString(companyValue);
                Date date = DateUtils.datestrToDate(datetime,"yyMMddHHmmss");
                pointPositionService = SpringBeanUtils.getBean(PointPositionService.class);
                monitorService = SpringBeanUtils.getBean(MonitorService.class);
                QueryWrapper<PointPosition> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("device_id",devicId);
                PointPosition position = pointPositionService.getOne(queryWrapper);
                if(ToolsUtils.isNotEmpty(position)){
                    Monitor monitor = new Monitor();
                    monitor.setMonitorType(position.getId());
                    monitor.setPointId(position.getPointsId());
                    monitor.setEarly(value);
                    if(ToolsUtils.isNotEmpty(position.getInitialValue())){
                        value = value.subtract(position.getInitialValue());
                    }
                    monitor.setActual(value);
                    monitor.setCreateTime(date);
                    List<Monitor> monitorList = new ArrayList<>();
                    monitorList.add(monitor);
                    monitorService.addMonitor(monitorList);
                }
                log.info("设备id为{}的传感器在{}时获取的到的测量数据为{}{}",devicId,DateUtils.dateToDateStr(date,DateUtils.YYYY_MM_DD_HH_MM_SS),value.doubleValue(),company);
            });
        }
    }

    /**
     * 服务端给客户端发送消息
     * @param channelId
     * @param msg
     * @throws Exception
     */
    public void channelWrite(ChannelId channelId, Object msg) throws Exception {
        Channel channel = ChannelMap.getChannelMap().get(channelId);
        if (channel == null) {
            log.info("通道:{},不存在",channelId);
            return;
        }
        if (msg == null || msg == "") {
            log.info("服务端响应空的消息");
            return;
        }
//        ByteBuf byteBuf = PooledByteBufAllocator.DEFAULT.buffer();
//        byte[] bytes = msg.toString().getBytes();
//        byteBuf.writeBytes(bytes);
//        //将客户端的信息直接返回写入ctx
//        channel.write(byteBuf)
        channel.write(msg)
                .addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) {
                // 回调
                if (future.isSuccess()) {
                    log.info("请求netty服务器，消息发送成功！{}",msg);
                } else {
                    log.info("请求netty服务器，消息发送失败！");
                }
            }
        });
        //刷新缓存区
        channel.flush();
    }

    /**
     * 长时间无心跳,回收通道
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        String socketString = ctx.channel().remoteAddress().toString();
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                log.info("Client:{},READER_IDLE 读超时",socketString);
                ctx.disconnect();
                Channel channel = ctx.channel();
                ChannelId id = channel.id();
                ChannelMap.removeChannelByName(id);
            } else if (event.state() == IdleState.WRITER_IDLE) {
                log.info("Client:{}, WRITER_IDLE 写超时",socketString);
                ctx.disconnect();
                Channel channel = ctx.channel();
                ChannelId id = channel.id();
                ChannelMap.removeChannelByName(id);
            } else if (event.state() == IdleState.ALL_IDLE) {
                log.info("Client:{},ALL_IDLE 总超时",socketString);
                ctx.disconnect();
                Channel channel = ctx.channel();
                ChannelId id = channel.id();
                ChannelMap.removeChannelByName(id);
            }
        }
    }

    /**
     * 发生异常会触发此函数
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        log.info("{}:发生了错误{},此连接被关闭。此时连通数量:{}",ctx.channel().id(),cause.getMessage(),ChannelMap.getChannelMap().size());
    }
}
